﻿using System;
using System.IO;
using System.Text;
using System.Xml;
namespace YanZhiwei.DotNet2.Utilities.Common
{
    /// <summary>
    /// help:
    /*XPath 是XML的查询语言，和SQL的角色很类似。以下面XML为例，介绍XPath 的语法。
     * http://www.w3school.com.cn/xquery/index.asp
     * 
     <?xml version="1.0" encoding="ISO-8859-1"?>     
     <catalog>     
       <cd country="USA">     
         <title>Empire Burlesque</title>     
         <artist>Bob Dylan</artist>     
         <price>10.90</price>    
       </cd>     
       <cd country="UK">     
         <title>Hide your heart</title>   
          <artist>Bonnie Tyler</artist>   
          <price>9.90</price>     
       </cd>     
       <cd country="USA">    
         <title>Greatest Hits</title>   
          <artist>Dolly Parton</artist>    
         <price>9.90</price>    
       </cd>   
     </catalog>
     定位节点
     XML是树状结构，类似档案系统内数据夹的结构，XPath也类似档案系统的路径命名方式。不过XPath 是一种模式(Pattern)，可以选出 XML档案中，路径符合某个模式的所有节点出来。例如要选catalog底下的cd中所有price元素可以用：
     catalog/cd/price
     如果XPath的开头是一个斜线（/）代表这是绝对路径。如果开头是两个斜线（//）表示文件中所有符合模式的元素都会被选出来，即使是处于树中不同的层级也会被选出来。以下的语法会选出文件中所有叫做cd的元素（在树中的任何层级都会被选出来）：
     //cd
     选择未知的元素
     使用星号（Wildcards,＊）可以选择未知的元素。下面这个语法会选出/catalog/cd 的所有子元素：
     catalog/cdprice
     以下的语法会选出有两层父节点，叫做price的所有元素。
     price
     以下的语法会选择出文件中的所有元素。
     //*
     要注意的是，想要存取不分层级的元素，XPath语法必须以两个斜线开头(//)，想要存取未知元素才用星号(*)，星号只能代表未知名称的元素，不能代表未知层级的元素。
     选择分支
     使用中括号可以选择分支。以下的语法从catalog的子元素中取出第一个叫做cd的元素。XPath的定义中没有第0元素这种东西。
     catalog/cd[1]
     以下语法选择catalog中的最后一个cd元素：（XPathj并没有定义 first() 这种函式喔，用上例的 [1]就可以取出第一个元素。
     catalog/cd[last()]
     以下语法选出含有price子元素的所有/catalog/cd元素。
     catalog/cd[price]
     以下语法选出price元素的值等于10.90的所有/catalog/cd元素
     catalog/cd[price=10.90]
     以下语法选出price元素的值等于10.90的所有/catalog/cd元素 的price元素
     catalog/cd[price=10.90]/price
     选择一个以上的路径
     使用Or操作数(|)就可以选择一个以上的路径。例如：
     catalog/cd/title | catalog/cd/artist
     选择所有title以及artist元素
     //title | //artist
     选择所有title以及artist以及price元素
     //title | //artist | //price
     选择属性
     在XPath中，除了选择元素以外，也可以选择属性。属性都是以@开头。例如选择文件中所有叫做country的属性：
     //@country
     选择所有含有country这个属性的cd元素：
     //cd[@country]
     以下语法选择出含有属性的所有cd元素
     //cd[@*]
     以下语法选择出country属性值为UK的cd元素
     //cd[@country='UK']
     只要掌握了xpath语法，理论上你就可以访问xml文件中的任意节点和任意值
     XmlNode xmlnode = xmldoc.SelectSingleNode("//document/section[@id='pla-01']/data[@id='fou_scc_code']");
     */
    /*
     *demo
      XMLToolV2 _xmlHelper = new XMLToolV2(@"C:\xmlText.xml");
      _xmlHelper.Create("aa", "UTF-8");
      XmlElement _xName = _xmlHelper.CreateElec("Name", "YanZhiwei");
      _xmlHelper.CreateElec(_xName, "address", "Shanghai");
      _xmlHelper.SetAttribute(_xName, "Gender", "man");
      _xmlHelper.Save();
      --------------------------------------------
      XmlNode _xSelectNode = _xmlHelper.Select("//Name[@Gender='man']");
      XmlNodeList _xNodeList = _xmlHelper.SelectNodeList("aa/Name");
      Console.WriteLine(_xSelectNode.InnerText);
      string _xmlString = _xmlHelper.ShowXml();
      Console.WriteLine(_xmlString);
      Console.WriteLine(XMLToolV2.FormatXml(_xmlString, ""));
      */
    /// </summary>
    public class XMLHelper
    {
        static string _xmlPath;
        static XmlDocument _xmlDoc { get; set; }
        static XmlElement _xmlRoot;
        public XMLHelper(string xmlPath)
        {
            _xmlPath = xmlPath;
            LoadXmlDoc();
        }
        /// <summary>
        /// xpath语法筛选节点
        /// </summary>
        /// <param name="xPath">xpath语法</param>
        /// <returns>XmlNode</returns>
        public XmlNode Select(string xPath)
        {
            return _xmlDoc.SelectSingleNode(xPath);
        }
        /// <summary>
        /// xpath语法筛选节点集合
        /// </summary>
        /// <param name="xPath">xpath语法</param>
        /// <returns>XmlNodeList</returns>
        public XmlNodeList SelectNodeList(string xPath)
        {
            return _xmlDoc.SelectNodes(xPath);
        }
        /// <summary>
        /// 创建xml文件
        /// </summary>
        /// <param name="rootName">根节点名称</param>
        /// <param name="encode">encode编码</param>
        public void Create(string rootName, string encode)
        {
            CreateXmlDoc(rootName, encode);
        }
        /// <summary>
        /// 创建xml文件
        /// </summary>
        /// <param name="rootName">根节点名称</param>
        /// <param name="encode">encode编码</param>
        private void CreateXmlDoc(string rootName, string encode)
        {
            if (!File.Exists(_xmlPath))
            {
                _xmlDoc = new XmlDocument();
                XmlDeclaration _xmldecl = _xmlDoc.CreateXmlDeclaration("1.0", encode, null);
                _xmlDoc.AppendChild(_xmldecl);
                _xmlRoot = _xmlDoc.CreateElement("", rootName, "");
                _xmlDoc.AppendChild(_xmlRoot);
            }
        }
        /// <summary>
        /// 加载xml文件
        /// </summary>
        public void LoadXmlDoc()
        {
            if (File.Exists(_xmlPath))
            {
                _xmlDoc = new XmlDocument();
                _xmlDoc.Load(_xmlPath);
                _xmlRoot = _xmlDoc.DocumentElement;
            }
        }
        /// <summary>
        /// 保存xml操作到文件
        /// </summary>
        public void Save()
        {
            if (_xmlDoc != null)
            {
                _xmlDoc.Save(_xmlPath);
            }
        }
        /// <summary>
        /// 创建节点XmlElement『添加在跟节点后』
        /// </summary>
        /// <param name="elecName">节点名称</param>
        /// <param name="elecValue">节点数值</param>
        /// <returns>XmlElement</returns>
        public XmlElement CreateElec(string elecName, string elecValue)
        {
            XmlElement _xElec = CreateElement(_xmlRoot, elecName, elecValue);
            return _xElec;
        }
        /// <summary>
        /// 在某个节点下添加节点
        /// </summary>
        /// <param name="_xmldocSelect">父节点</param>
        /// <param name="elecName">节点名称</param>
        /// <param name="elecValue">节点数值</param>
        /// <returns>XmlElement</returns>
        private XmlElement CreateElement(XmlNode _xmldocSelect, string elecName, string elecValue)
        {
            if (_xmldocSelect != null)
            {
                XmlElement _xElec = _xmlDoc.CreateElement(elecName);
                _xElec.InnerText = elecValue;
                _xmldocSelect.AppendChild(_xElec);
                return _xElec;
            }
            return null;
        }
        /// <summary>
        /// 在某个节点下添加节点
        /// </summary>
        /// <param name="_xmldocSelect">父节点</param>
        /// <param name="elecName">节点名称</param>
        /// <param name="elecValue">节点数值</param>
        /// <returns>XmlElement</returns>
        public XmlElement CreateElec(XmlElement xmlParentElec, string elecName, string elecValue)
        {
            if (xmlParentElec != null)
            {
                XmlElement _xElec = CreateElement(xmlParentElec, elecName, elecValue);
                return _xElec;
            }
            return null;
        }
        /// <summary>
        /// 设置某个节点属性
        /// </summary>
        /// <param name="xElement">需要设置属性的节点</param>
        /// <param name="attrName">属性名称</param>
        /// <param name="attrValue">属性数值</param>
        public void SetAttribute(XmlElement xElement, string attrName, string attrValue)
        {
            if (xElement != null)
            {
                xElement.SetAttribute(attrName, attrValue);
            }
        }
        /// <summary>
        /// 检查根据xpath语法筛选下是否存在该节点
        /// </summary>
        /// <param name="xpath"></param>
        /// <returns></returns>
        public int Check(string xpath)
        {
            return SelectNodeList(xpath).Count;
        }
        /// <summary>
        /// 将xml内容转换string输出
        /// </summary>
        /// <returns></returns>
        public string ShowXml()
        {
            if (_xmlDoc != null)
            {
                return _xmlDoc.OuterXml;
            }
            return string.Empty;
        }
        /// <summary>
        /// 格式化xml内容显示
        /// </summary>
        /// <param name="xmlString">xml内容</param>
        /// <param name="encode">encode编码</param>
        /// <returns>string</returns>
        public static string FormatXml(string xmlString, string encode)
        {
            MemoryStream _mstream = new MemoryStream();
            XmlTextWriter _writer = new XmlTextWriter(_mstream, null);
            XmlDocument _xDoc = new XmlDocument();
            _writer.Formatting = Formatting.Indented;
            _xDoc.LoadXml(xmlString);
            _xDoc.WriteTo(_writer);
            _writer.Flush();
            _writer.Close();
            Encoding _encoding = Encoding.GetEncoding(encode);
            string _xmlString = _encoding.GetString(_mstream.ToArray());
            _mstream.Close();
            return _xmlString;
        }
        /// <summary>
        /// 遍历节点集合
        /// </summary>
        /// <param name="nodeList">节点集合</param>
        /// <param name="xmlNode">需要对遍历节点的处理『委托』</param>
        public static void Loop(XmlNodeList nodeList, Action<XmlNode> xmlNode)
        {
            if (nodeList != null)
            {
                foreach (XmlNode xNode in nodeList)
                {
                    xmlNode(xNode);
                }
            }
        }
    }
}